/**
     * JavaScript原生String对象扩展。 
     * @class String
     * @module JavaScript core extension 
     */   
    
    /**
     * string format.
     * eg:'aaaaa{0}bbbbb{1}cccc{2}'.format(100,200,300) 
     */
    String.prototype.format = function() {
        if (arguments.length === 0){
            var str = this,
                isFormatted = false;            
            //'Date is {Time:1375286400000}'.format()
            str = str.replace( /{([^}]+)}/g, function(matchedStr, subMatchedStr1){            
                var expressionMembers = subMatchedStr1.split(':'),
                    converterName = expressionMembers[0],
                    rawValue = expressionMembers[1];
                if(converterName && rawValue){
                    isFormatted = true;                
                    var converter = String.prototype['to' + converterName.trim().capitalize()];
                    if(converter){
                        return converter.call(rawValue.trim().replace(/^\"|\"$/gi,'').replace(/^\'|\'$/gi,''));
                    } else {
                        return rawValue;
                    }
                }
            });
            return isFormatted ? str : this;    
        }
        if( Object.prototype.toString.call(arguments[0]) === '[object Object]' ){
            return this.formatByMap(arguments[0]);
        } else {
            for (var s = this, i = 0, len = arguments.length; i < len; i++) {
                s = s.replace(new RegExp("\\{" + i + "\\}", "g"), arguments[i]);
            }
            return s;
        }
    };
    /**
     * eg: 'aaaaa{key1}bbbbb{key2|key4}cccc{key3.item}'.format({key1:100, key2:200, key3: { item:300 }, key4 : 400 })
     */
    String.prototype.formatByMap = function(mapObj) {
        var str = this;
        if (mapObj) {        
            str = str.replace( /{([^}]+)}/g, function(matchedStr, subMatchedStr){
                var subMatchedStrArr = subMatchedStr.split('|'),
                    expressionValue;
                for(var i=0; i<subMatchedStrArr.length; i++){
                    var subMatchedStr1 = subMatchedStrArr[i];
                    var expressionMembers = subMatchedStr1.split('.');
                    expressionValue = mapObj;
                    
                    do{
                        expressionValue = expressionValue[expressionMembers.shift()];    
                    } while( expressionValue && expressionMembers.length );
                    
                    if(!Type.isUndefined(expressionValue)){
                        break;
                    }
                }               
                return Type.isUndefined(expressionValue) ? '' : expressionValue;
            });
        }
        return str;
    };
    
    /**
     * 判断字符串是否在指定的集合中.
     */
    String.prototype['in'] = function(collection){
        for(var i=0,len=collection.length; i<len; i++){
            if(this == collection[i]){
                return true;
            }
        }
        return false;
    };
    
    /**
     * 判断字符串是否在指定的集合中.
     */
    String.prototype.belongTo = String.prototype.at = String.prototype['in'];
    
    //TODO 老版本兼容性代码，新项目中去掉.
    
    /**
     * 生成唯一ID.
     */
    String.newGuid = (function() {        
        var prevGenTime = null,
            prevGuidIndex = 0;
        /**
         * @prefix : {String} : 生成的Guid的前缀，如：pre_1379914908358_0。
         * @radius : {Number} : 为了缩短guid的长度，将guid转换成数字后，再转换成指定进制(2-36)的数值。
         * 
         * @return : {String} : GUID。
         */
        return function(prefix/* or radius */) {
            var currentGenTime = Date.tick();
            if( prevGenTime !== currentGenTime ){
                prevGenTime = currentGenTime;
                currentGuid = 0;
            }    
            if(Type.isNumber(prefix)){
                var radius = prefix.constraintRegion(2, 36).toInt();            
                return parseInt(prevGenTime + '' + ( currentGuid++ )).toString(radius);
            } else {
                return (prefix || '' ) + prevGenTime + '_' + ( currentGuid++ );
            }
        };
    })();
    String.newId = String.newID = String.newid = String.id = String.newGuid;
    
    String.isExpression = function(str){
        if(str && Type.isString(str)){
            return !/^\w+$/.test(str) && ( str.indexOf('@') === 0 || str.indexOf('.') > -1 );
        } else {
            return false;
        }
    };


/**
     * 反转字符串.
     */
    String.prototype.reverse = function(){
        return this.split('').reverse().join('');
    };
    
    /**
     * @param prefix {String|Regexp}
     */
    String.prototype.startsWith = function(prefix) {
        var pos = this.indexOf(prefix);
        return pos === 0 || (pos && pos.index == 0);
    };
    
    /**
     * 
     */
    String.prototype.endsWith = function(suffix) {
        return this.indexOf(suffix, this.length - suffix.length) !== -1;
    };
    
    /**
     * 获取扩展名.
     */
    String.prototype.getExtension = function() {
        var strParts = this.split('.');
        if (strParts.length > 1) {
            return strParts[strParts.length - 1];
        } else {
            return "";
        }
    };
    
    String.prototype.removeExtension = function(){
        return this.split('.')[0];
    };
    
    /**
     * 去掉两边的空格, 或去掉字符串两边指定的正则匹配.
     */
    String.prototype.trim = function(regExp) {    
        if(Type.isString(regExp)){
            return this.replace(new RegExp('^(' + regExp + ')|(' + regExp + ')$', 'gi'), '');
        } else if(Type.isRegExp(regExp)){
            return this.replace(new RegExp('^(' + regExp.source + ')|(' + regExp.source + ')$', 'gi'), '');
        } else {
            return this.replace(/^[\s　]+|[\s　]+$/g, '');
        }
    };
    
    String.prototype.getMatches = function(regExp, isGetDetails){
//        return regExp.getMatches(this, isGetDetails);
        return ujs.regx.getMatches(regExp, this, isGetDetails);
    };
    
    /**
     * 获取字符中尾部的数字.
     */
    String.prototype.getSuffixNumber = function(){
//        var matches = /[0-9.]+$/.getMatches(this.trim());
        var matches = ujs.regx.getMatches(/[0-9.]+$/, this.trim());
        return matches && matches.length ? parseFloat(matches[0]) : null;      
    };
    
    /**
     * Return a new string without leading whitespace
     */
    String.prototype.trimLeft = function() {
        return this.replace(/^\s+/,"");
    };
    
    /**
     * Return a new string without trailing whitespace
     */
    String.prototype.trimRight = function() {
        return this.replace(/\s+$/,"");
    };
    
    /**
     * 去掉字符串两边的空标记.
     */
    String.prototype.trimEmptyTags = function(){
        return this.trim().trim(/(<(?!\/)[^>]+>)+(<\/[^>]+>)+/);    
    };
    
    /**
     * 使首字母大写，其它字母小写.
     */
    String.prototype.capitalize = function() {
        return this.toLowerCase().replace(/(^\w{1})/gi, function($1) {
            return $1.toUpperCase();
        });
    };
    
    /**
     * 使首字母小写，其它字母不变.
     */
    String.prototype.lowercaseFirstChar = function() {
        return this.replace(/(^\w{1})/gi, function($1) {
            return $1.toLowerCase();
        });
    };
    
    /**
     * 使首字母大写，其它字母不变.
     */
    String.prototype.uppercaseFirstChar = function() {
        return this.replace(/(^\w{1})/gi, function($1) {
            return $1.toUpperCase();
        });
    };
    
    /**
     * 去掉字符串中的脚本标签。
     */
    String.prototype.stripScript = function(){
        return this.replace(/<script[^>]*>([\s\S]*?)<\/script>/mgi, '');
    };

    /**
     * 将字符串中的url地址转换成链接。
     */
    String.prototype.httpHtml = function(){
        var regExp = /(http:\/\/|https:\/\/|www\.)((\w|=|\?|\;|\#|\~|\^|\$|\%|\*|\!|\:|\@|\(|\)|\.|\/|&|-)+)/gi;
        //return this.replace(regExp, '<a href="$1$2" target="_blank">$1$2</a>');
        return this.replace(regExp, function(url, $1, $2){
            if($1.toLowerCase() == 'www.'){
                $1 = 'http://www.';
            }
            url = $1 + $2;
            return '<a href="{0}" target="_blank">{0}</a>'.format(url);
        });
    };
    
    /**
     * 
     */
    String.prototype.safeString = function() {
        var escape = {
            "&" : "&amp;",
            "<" : "&lt;",
            ">" : "&gt;",
            '"' : "&quot;",
            "'" : "&#x27;",
            "`" : "&#x60;"
        };
        return this.replace(/[&<>"'`]/g, function(chr) {
            return escape[chr] || "&amp;";
        });
    };
    
    /**
     * 
     */
    String.prototype.unsafeString = function() {
        var _this = this,
            unescape = {    
                '&lt;' : '<',
                '&gt;' : '>',
                '&quot;' : '"',
                '&#x27;' : "'",
                '&#x60;' : '`'            
            };
                    
        Object.each(unescape, function(key, value){
            _this = _this.replace(new RegExp(key, 'gi'), value);
        });
        return _this.replace(new RegExp('&amp;', 'gi'), '&');
    };
    
    /**
     * 将字符串中的Html标记去掉.
     * @param isIgnoreLinebreak {Boolean} 是否忽略换行符.
     */
    String.prototype.toPlainText = function(isIgnoreLinebreak) {
        if(isIgnoreLinebreak === true){
            var tmp = document.createElement("DIV");
            tmp.innerHTML = this;
            return tmp.textContent || tmp.innerText || '';    
        } else {
            return this.replace(
                /<(\w+)>(((?!<\/\1>).)*)<\/\1>/mgi, //提取标记内容.
                function(m,p1,p2){
                    return '\n' + p2 + '\n';
                }
            )
            .replace(/(\n+)|<br>|<br\/>/gi,'\n')    //转换<br/>
            .replace(/\&nbsp;/gi, ' ')              //转换&nbsp;
            .replace(/(<(?!\/)[^>]+>)+(<\/[^>]+>)+/mgi, '') //去掉空标记.
            .replace(/<\/?[^>]+>/mgi, '');  //去掉其它标记头或尾.
        }
    };
    
    /**
     * Convert HTML breaks to newline
     */
    String.prototype.br2nl = function() {
        return this.replace(/<br\s*\/?>/mg,"\n");
    };
    
    /**
     * Convert newline to HTML breaks
     */
    String.prototype.nl2br = function() {
        return this.replace(/\n/g, "<br/>");
    };
    
    /**
     * Convert HTML whitespace to normal whitespace
     */
    String.prototype.nbsp2s = function() {
        return this.replace(/\&nbsp;/mg," ");
    };
    
    /**
     * 网页中高亮显示字符串中的关键词.
     * 关键词以 #em# 开始，以 $em$ 结束。
     * 如将：Json path "#em#code$em$" is operation  Ids Response: Json path "#em#code$em$
     * 转化成：Json path "<b style="color:#fc4444">code</b>" is operation  Ids Response: Json path "<b style="color:#fc4444">code</b>
     * 
     * 使用场景：在使用 lucene 做搜索时，若需要支持使用 lucene 语法模糊查询，则纯前端难以完成关键词高亮显示功能，需要由server端完成高亮关键词指定，由前端显示。
     * @method highlight
     * @param keys {Array}
     * @return {String}
     */
    String.prototype.highlight = function(keys){
        var result = this,
            wrapperHtml = '<b style="color:#fc4444">{0}</b>';
            
        if(Type.isArray(keys)){
            for(var i=0; i<keys.length; i++){
                result = result.replace(new RegExp(keys[i], 'igm'), function(match){
                    return wrapperHtml.format(match);
                });
            }
        } else {
            var key = keys + '';
            return this.highlight([ key ]);
        }    
        return result.replace(/#em#(.+?)\$em\$/gi, wrapperHtml.format('$1'));
    };
    
    /**
     * 将url地址补全为合法地址.
     * @param {String} : defProtocol 默认补全的协议名.
     */
    String.prototype.toLegalURL = function(defProtocol) {
        if(this.startsWith(/((\w)+:\/\/)|\/\//)){
            //以 word:// 或 //开头时.
            return this;
        }
        defProtocol = defProtocol || 'http';     
        var protocolPrefixs = ['http://', 'https://', 'ftp://', 'telnet://', 'pop://'];
        for(var i=0; i<protocolPrefixs.length; i++){
            if(this.toLowerCase().startsWith(protocolPrefixs[i])){
                return this;
            }
        }
        return defProtocol + '://' + this;
    };
    
    /**
     * 去掉url前缀
     * TODO 使支持https,ftp等其它协议，使用正则表达式匹配算法，性能优化.
     */
    String.prototype.removeUrlPrefix = function(url){
        url = url.replace(/：/g,":").replace(/．/g,".").replace(/／/g,"/").trim().toLowerCase();
        while(url.indexOf("http://") === 0){
            url = url.replace(/http:\/\//i,"");
        }
        return url;
    }
    
    /**
     * 计算字符串的长度.
     */
    String.prototype.measureText = (function(){
        var ctx = document.createElement("canvas").getContext("2d");    
        return function(font){                
            if(font){
                ctx.font = font;
            }
            return ctx.measureText(this).width;        
        };
    })();
    
    /**
     * Count the number of times a substring is in a string.
     */
    String.prototype.substrCount = function(s) {
        return this.length && s ? (this.split(s)).length - 1 : 0;
    };
    
    /**
     * 字符串长度截取
     */
    String.prototype.cut = function(len, postfix) {
        if (typeof postfix === 'undefined') {
            postfix = '...';
        }
        var str = this,
            patrn = /[^\x00-\xff]/,
            chr, strCount = 0,
            result = [],
            i = 0,
            strLen = str.length;
        for (; i < strLen && strCount < len; i++) {
            chr = str.substr(i, 1);
            patrn.exec(chr) ? strCount += 2 : strCount++;
            if (strCount > len) break;
            result.push(chr);
        }
        result = result.join('');
        return result + (result.length === strLen ? '': postfix);
    };
    
    /**
     * 使indexOf支持Regex查找.
     */
    String.prototype.indexOf = (function(){
        var originalIndexOf = String.prototype.indexOf;
        return function( searchValue, fromIndex ){
            if(typeof searchValue === 'number'){
                searchValue = searchValue + '';
            }
            
            if(typeof searchValue === 'string'){
                return originalIndexOf.call(this, searchValue, fromIndex);
            }
            
            if(searchValue instanceof RegExp){
                if( fromIndex === undefined ){
                    fromIndex = 0;
                }
                
                var searchStr = this.substr(fromIndex),
                    match = searchValue.exec(searchStr);
                
                return ( !match ) ? -1 : {
                    str : match[0],
                    index : fromIndex + match['index']   
                };
            }
            
            throw 'Invalid Search Value, from: String.prototype.indexOf';
        }; 
    })();
    
    /**
     * 截取字符串并显示省略号.
     */
    String.prototype.ellipse = function(len, delimiter){
        if(!len || this.length <= len){
            return this;
        }
        delimiter = delimiter || '...';    
        return this.substr(0,len) + delimiter;    
    };
    
    /**
     * 判断当前字符串中是否包含指定的匹配.
     * @param {RegExp} / {String} regExp
     * @return {Boolean}.
     */
    String.prototype.contains = function(regExp /*or str*/, sep, isCaseSensitive){
        var _this = this;
        if(isCaseSensitive !== true && Type.isString(regExp)){
            regExp = regExp.toLowerCase();
            _this = _this.toLowerCase();        
        }
        if(!sep){
            //search() 方法不执行全局匹配，它将忽略标志 g。
            //它同时忽略 regexp 的 lastIndex 属性，并且总是从字符串的开始进行检索，这意味着它总是返回 stringObject 的第一个匹配的位置。     
            return _this.search(regExp /*or str*/) > -1;
        } else {
            if(Object.prototype.toString.call(regExp) == '[object RegExp]'){
                throw '使用分隔符时不支持正则表达式搜索.';
            }
            var str = regExp;
            return (sep + _this + sep).indexOf(sep + str + sep) > -1;
        }
    };
    
    

    /**
     * 清理字符串,将字符串中的连续空字符合并为一个空格.
     */
    String.prototype.clean = function(){
        return String(this).replace(/\s+/g, ' ').trim();
    };
    
    /**
     * 获取当前字符串中的所有模式匹配. 
     * @param {RegExp} regExp
     * @param {Boolean} isGetDetails    : 仅返回匹配字符串，还是每次匹配的详细信息，默认仅返回匹配字符串。
     * 
     * @return {Array:Object} / {Array:String}
     */
    String.prototype.getMatches = function(regExp, isGetDetails){
        if(regExp && regExp instanceof RegExp){
      //      return regExp.getMatches(this, isGetDetails);
return            ujs.regx.getMatches(regExp, this, isGetDetails);
        }
        throw 'Require RegExp object parameter, from string.prototype.getMatches.';
    };
    
    String.prototype.toInt = function(base){
        return parseInt(this, base || 10);
    };
    
    String.prototype.toFloat = function(){
        return parseFloat(this) / ( this.endsWith('%') ? 100 : 1 );
    };
    
    String.prototype.toBool = function(){
        return this.indexOf('true') === 0;
    };
    
    String.prototype.toTime = function(){
        return Date.from(parseInt(this)).format(window.ujsConfig ? window.ujsConfig.dateTimeFormat : "dd-MN-yyyy h:m");     
    };
    
    String.prototype.toJSON = function(){
        return JSON.parse(this);
    };
    
    /**
     * 获取字符串的后缀数字，如：pageContainer59，中，获取59.
     */
    String.prototype.suffixNumber = function(){
        return parseFloat( this.trim().replace(/^[^\d]+/, '') ) || 0;
    };
    
    /**
     * 判断字符串是否是纯数字.
     */
    String.prototype.isNumber = function(){
        return !!this.match(/^\d+$/);
    };
    
    /**
     * 判断字符串是否是数字区间. 如:
     * [4000-5000], (4000-5000), (4000-5000], [4000-5000)
     */
    String.prototype.isNumberRegion = function(){
        var numberRegionRegexp = /^(\[|\()\d+\-\d+(\]|\))$/;
        return numberRegionRegexp.test(this);
    };
    
    
    
    String.prototype.test = function(regex, params){
        regex = Type.isRegExp(regex) == 'regexp' ? regex : new RegExp('' + regex, params);
        return regex.test(this);
    },
    
    
    /**
     * 将字符串当作成员调用表达式求值。
     * eg : 'ujsConfig.version'.evaluate().
     * @param {Object} context : 默认window.
     */
    String.prototype.evaluate = function(context){
        var self = this.trim();
        if(String.isExpression(self)){
            if(self.indexOf('@') === 0){
                var value = self.substring(1);
                if(value.indexOf('@') === 0){
                    value = eval('(' + value.substring(1) + ')');
                }    
            } else {
                var value = context || window,
                    membersChain = this.trim().split('.');
                while(membersChain.length && value){
                    value = value[membersChain.shift()];
                }  
            }
            return value;
        } else {
            return self;
        }
    };
    /**
     * 解析字符串中HTML Entity表示的字符.
     */
    String.prototype.parseHTMLEntity = function(){
        var div = document.createElement('div');
        div.innerHTML = this;
        return div.innerHTML;    
    };
    
    /**
     * 获取字符串最后的文件名，如在字符串http://acucom.acucom.org/ui/index.html中获取index.html。
     */
    String.prototype.getFileName = function(){
        return this.split(/\/|\\/gi).last();
    };
    
    /**
     * 去掉字符串中所有的空格.
     */
    String.prototype.shrink = function(){
        return this.replace(/\s+/g,'');  
    };
    




























/**
     * 转换信息的数量单位.
     * @method toDiskSize.
     * @param {Object} unit : K/M/G/T，默认为 M.
     * @param {Object} precision    : 结果精度，默认为 2.
     * @param {Object} isShowUnit   : 是否显示单位(KB/MB/GB/TB),默认显示.
     * @return {String}
     */
    Number.prototype.toDiskSize = function(unit, precision, isShowUnit){
        var rates = {
            'K' : 1024,
            'M' : Math.pow(1024, 2),
            'G' : Math.pow(1024, 3),
            'T' : Math.pow(1024, 4)
        };
        unit = ( unit || 'M' ).toUpperCase();
        if(!rates[unit]){
            throw '不正确的信息数量单位.';
        }
        precision = precision || 2;
        isShowUnit = isShowUnit !== false;
        return ( this/rates[unit] ).toFixed( precision ) + ( isShowUnit ? unit+'B' : '' );
    };
    Number.toDiskSize  = function(number){
        return Number.prototype.toDiskSize.apply(number || 0, Object.toArray(arguments).slice(1));    
    };
    
    /**
     * 取整.
     * @method toInt
     * @return {Number}
     */
    Number.prototype.toInt = function(base){
        return parseInt(this, base || 10);
    };
    
    /**
     * 获取浮点数.
     */
    Number.prototype.toFloat = function(){
        return parseFloat(this);
    };
    
    /**
     * Return true if number is a float
     * @method isFloat
     * @return {Boolean}
     */
    Number.prototype.isFloat = function() {
        return /\./.test(this.toString());
    };
    
    /**
     * Return true if number is an integer
     * @method isInteger
     * @return {Boolean}
     */
    Number.prototype.isInteger = function() {
        return Math.floor(this) == this;
    };
    
    /**
     * 判断数字是否在指定的区间内.
     * [4000-5000], (4000-5000), (4000-5000], [4000-5000)
     */
    Number.prototype.isInRegion = function(numberRegionStr){
        var flag = false;
        if(numberRegionStr && numberRegionStr.isNumberRegion()){
            var numberParts = numberRegionStr.split('-'),
                numberPartOne = numberParts[0],
                numberPartTwo = numberParts[1];                
            var numberPartOneRegionType = numberPartOne.substr(0,1);
            var numberPartOneValue = parseInt(numberPartOne.substr(1));
            if((numberPartOneRegionType == '[' && this >= numberPartOneValue) || (numberPartOneRegionType == '(' && this > numberPartOneValue)) {            
                var numberPartTwoRegionType = numberPartTwo.substr(numberPartTwo.length - 1,1);
                var numberPartTwoValue = parseInt(numberPartTwo.substr(0, numberPartTwo.length - 1));
                if((numberPartTwoRegionType == ']' && this <= numberPartTwoValue) || (numberPartTwoRegionType == ')' && this < numberPartTwoValue)) {
                    flag = true;
                }
            } 
        }
        return flag;
    };
    
  
    /**
     * 将指定的函数执行n次.
     */
    Number.prototype.times = function(fn, ctx){
        for(var i = 0; i < this; i++){ 
            fn.call(ctx, i, this);
        }
    };
    Number.prototype.each = Number.prototype.times;
    
    /**
     * 将 10 转换成 '+10',
     * 将 -10 转换成 '-10'.
     * @method toSignedNumberString
     * @param zeroPrefix {String} : 0之前的字符，一般用 +号，默认为空字符串。
     * @return {String}
     */
    Number.prototype.toSignedNumberString = function(zeroPrefix){    
        if(this > 0){
            return '+' + String(this);        
        } else if(this == 0){
            return (zeroPrefix || '') + String(this);
        } else {
            return String(this);
        }
    };
    
    /**
     * 左填充。
     * @method paddingLeft
     * @param paddingStr {String|Function} 填充值，默认为0.
     * @return {String}
     */
    Number.prototype.paddingLeft = function(paddingStr /* or paddingFun */){
        if(!paddingStr){
            paddingStr = '0';
        }
        if(!Type.isFunction(paddingStr)){
            var paddingFun = function(num){
                return num.inRegion(0, 9, true) ? (paddingStr + num) : num + '';
            };
        } else {
            var paddingFun = paddingStr;
        }
        return paddingFun(this);
    };
    
/**
     * 约束数值在指定的范围[min, max]。
     * @method constraintRegion
     * @param min {Number} 最小值.
     * @param max {Number} 最大值.
     * @return {Number}.
     */
    Number.prototype.constraintRegion = function(min, max){
        if(this < min){
            return min;
        } 
        if(this > max){
            return max;
        }   
        return this;
    };
    Number.prototype.limit = Number.prototype.constraintRegion;
    
    /**
     * 判断数值是否在指定的范围内[min, max]。
     * @method inRegion
     * @param min {Number} 最小值.
     * @param max {Number} 最大值.
     * @param isClosed {Boolean} 是否是闭区间，默认false.
     * @return {Boolean}.
     */
    Number.prototype.inRegion = function(min, max, isClosed){    
        return (isClosed === true && this >= min && this <= max) || (!isClosed && this > min && this < max );
    };
    
/**
     * 重写 toFixed 方法.
     * @method toFixed
     * @param num {Number} 指定保留几位小数.
     * @param isRemoveEndZero {Boolean} 是否去掉末尾的0.
     * @return {String} 
     */
    (function(){
        var originalToFixed = Number.prototype.toFixed;
        Number.prototype.toFixed = function(num, isRemoveEndZero){  
            var fixedValue = originalToFixed.call( Number(this), num || 2 );
            if(isRemoveEndZero){
                fixedValue = fixedValue / 1 + '';
            }       
            return fixedValue;
        };   
        Number.prototype.round = Number.prototype.toFixed; 
    })();
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
      /**
     * 获取  Month Name.
     * @method getMonthName
     */
    Date.prototype.getMonthName = function() {
        //使用entity表示字母，以避免冲突, 对应字母如下行注释代码。
        return ([
            '&#74;&#97;&#110;','&#70;&#101;&#98;','&#77;&#97;&#114;',
            '&#65;&#112;&#114;','&#77;&#97;&#121;','&#74;&#117;&#110;',
            '&#74;&#117;&#108;','&#65;&#117;&#103;','&#83;&#101;&#112;',
            '&#79;&#99;&#116;','&#78;&#111;&#118;','&#68;&#101;&#99;'
        ])[ this.getMonth() ];
        //return (['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'])[ this.getMonth() ];    
    };
    /**
     * 获取 Week Name.
     * @method getWeekName
     */
    Date.prototype.getWeekName = function(){
        //使用entity表示字母，以避免冲突, 对应字母如下行注释代码。
        return ([
            '&#83;&#117;&#110;', '&#77;&#111;&#110;', '&#84;&#117;&#101;', 
            '&#87;&#101;&#100;', '&#84;&#104;&#117;&#114;', '&#70;&#114;&#105;', 
            '&#83;&#97;&#116;'
        ])[ this.getDay() ];
        //return (['Sun', 'Mon', 'Tue', 'Wed', 'Thur', 'Fri', 'Sat'])[ this.getDay() ];
    };
    /**
     * 日期格式化.如:
     * alert(new Date().format("yyyy-MM-dd"));
     * alert(new Date("january 12 2008 11:12:30").format("yyyy-MM-dd hh:mm:ss")); 
     * @method format
     */
    Date.prototype.format = function(format) {
        var o = {  
            /**
             * 特殊的格式符，使用大写，如TZ, MN, WN。
             * MM为月份小于10时，前补零，如:2012-01-11。
             */
            'TZ' : parseInt( ujs.num.negetive(this.getTimezoneOffset()/60 )).toSignedNumberString('+'), //本地时间与 GMT 时间之间的时间差，以小时为单位.
            "q+" : Math.floor((this.getMonth() + 3) / 3), //quarter
            
            "y+" : this.getFullYear(),
            
            "MN" : this.getMonthName(), //month name
            "M+" : this.getMonth() + 1, //month number
            
            "d+" : this.getDate(), //day
            
            "WN" : this.getWeekName(),  //week
            
            "h+" : this.getHours(), //hour
            "H+" : (function(h){
                return h < 13 ? h : h - 12;
            })(this.getHours()), //hour 12小时制
            "m+" : this.getMinutes(), //minute
            'MP' : this.getMinutes() - (Date.nowDateObj()).getMinutes(),    //minutes passed
            "s+" : this.getSeconds(), //second
            "DP" : (function(dataObj){  //AM/PM
                return dataObj.getHours() < 12 ? 'AM' : 'PM';
            })(this)
        };
        for (var k in o) {
            if ( new RegExp( "(" + k + ")" ).test(format) ) {
                var matchStr = RegExp.$1; 
                format = format.replace(
                    matchStr, 
                    function(){
                        if(matchStr === 'yy'){
                            return o[k] % 100;
                        } else {
                            return ( matchStr.length > 1 && typeof(o[k]) == 'number' && o[k].inRegion(-1, 10) ) ? ('0' + o[k] ) : o[k];
                        }   
                    }                
                );
            }
        }
        return format;
    };
    
    /**
     * 时间增量.
     * @method add
     */
    Date.prototype.add = function(interval, number){ 
        switch( interval.toLowerCase() ){ 
            //年
            case "y" : 
            case "year" : 
                return new Date( this.setFullYear( this.getFullYear() + number ) );
            //月 
            case "m" : 
            case "month" :
                return new Date( this.setMonth( this.getMonth() + number ) );
            //天 
            case "d" :
            case "day" : 
                return new Date( this.setDate( this.getDate() + number ) );
            //周 
            case "w" :
            case "week" : 
                return new Date( this.setDate( this.getDate() + 7 * number ) );
            //小时 
            case "h" : 
            case "hour" :
                return new Date( this.setHours( this.getHours() + number ) );
            //分钟 
            case "n" : 
            case "minute" :
                return new Date( this.setMinutes( this.getMinutes() + number ) );
            //秒 
            case "s" :
            case "second" : 
                return new Date( this.setSeconds( this.getSeconds() + number ) );
            //毫秒 
            case "l" :
            case "millisecond" : 
                return new Date( this.setMilliseconds( this.getMilliseconds() + number ) ); 
        } 
    };
    
    /**
     * 获取当前时间与指定时间的差距.
     * @method DateDiff
     */
    Date.prototype.DateDiff = function( interval, date ){
        return Date.DateDiff(interval, this, date);
    };
    
    /**
     * 若当前时间对象为生日，获取其相关的年龄。
     * @method getAge
     */
    Date.prototype.getAge = function(){
        return Date.getAge(this);
    };
    
    /**
     * 获取当前时间的时间段易读提示.
     * 1、< 60s, 显示为     “刚刚”
     * 2、>= 1min && < 60 min, 显示与当前时间差      “XX分钟前”
     * 3、>= 60min && < 1day, 显示与当前时间差       “今天 XX:XX”
     * 4、>= 1day && < 1year, 显示日期       “XX月XX日 XX:XX”
     * 5、>= 1year, 显示具体日期       “XXXX年XX月XX日 XX:XX”
     * @method getTimeTooltip
     * @return {String}
     */
    Date.prototype.getTimeTooltip = function(formatsMap){
        var now = new Date();
        
        if(!Type.isObject(formatsMap)){
            formatsMap = Date.timeTooltipFormat['cn'];
        }    
         
        if(this.getFullYear() < now.getFullYear()){
            // >= 1year.
            return this.format(formatsMap['five']);        
        } else {
            var pastH = (now - this)/3600000;     
            if(pastH > now.getHours()){
                // >= 1day && < 1year            
                return this.format(formatsMap['four']);
            } else if (pastH >= 1) {
                // >= 60min && < 1day
                return this.format(formatsMap['three']);
            } else {
                var pastM = now.getMinutes() - this.getMinutes();
                if( pastM > 1 ){
                    // >= 1min && < 60min                
                    return this.format(formatsMap['two']);
                } else {
                    // < 60s.                
                    return this.format(formatsMap['one']);
                }
            }
        } 
    };
    
    /**
     * Return the ISO day number for a date
     * @method getISODay
     */
    Date.prototype.getISODay = function(utc) {
        // Native JS method - Sunday is 0, monday is 1 etc.
        var d = utc ? this.getUTCDay() : this.getDay();
        // Return d if not sunday; otherwise return 7
        return d ? d : 7;
    };
    
    /**
     * Retuns true if year is a leap year; otherwise false
     * @method isLeapYear
     */
    Date.prototype.isLeapYear = function(utc) {
        var y = utc ? this.getUTCFullYear() : this.getFullYear();
        return !(y % 4) && (y % 100) || !(y % 400);
    };
    
    /**
     * Return the number of days in the month
     * @method getDaysInMonth
     */
    Date.prototype.getDaysInMonth = function(utc) {
        var m = utc ? this.getUTCMonth() : this.getMonth();
        // If feb.
        if (m == 1)
            return this.isLeapYear() ? 29 : 28;
        // If Apr, Jun, Sep or Nov return 30; otherwise 31
        return (m == 3 || m == 5 || m == 8 || m == 10) ? 30 : 31;
    };
    
    /**
     * Gen the english suffix for dates
     * @method getDaySuffix
     */
    Date.prototype.getDaySuffix = function(utc) {
        var n = utc ? this.getUTCDate() : this.getDate();
        // If not the 11th and date ends at 1
        if (n != 11 && (n + '').match(/1$/))
          return 'st';
        // If not the 12th and date ends at 2
        else if (n != 12 && (n + '').match(/2$/))
          return 'nd';
        // If not the 13th and date ends at 3
        else if (n != 13 && (n + '').match(/3$/))
          return 'rd';
        else
          return 'th';
    };
    
    
    //--------------------------------------------------------------------------------------------------------------
    
    /**
     * 时间计时的初始时间.
     * Thu Jan 01 1970 08:00:00 GMT+0800 (China Standard Time)
     * 
     * @property originalStartDate
     * @type {Date}
     * @static
     * @default "new Date(0)"
     */
    Date.originalStartDate = new Date(0);
    
    /**
     * 构建Date对象.
     * [Number milliseconds,]
     * [String dateString,]
     * [Object yr_num, mo_num, day_num,], [Object hr_num, min_num, sec_num, ms_num] 
     * @method from
     * @static
     */
    Date.from = function(yr_num/*milliseconds or dateString*/, mo_num, day_num, hr_num, min_num, sec_num, ms_num){
        if(arguments.length === 1){
            return new Date(yr_num);    
        } else {
            return new Date( 
                yr_num, 
                mo_num === undefined ? null : mo_num, 
                day_num === undefined ? null : day_num, 
                hr_num === undefined ? null : hr_num, 
                min_num === undefined ? null : min_num, 
                sec_num === undefined ? null : sec_num, 
                ms_num === undefined ? null : ms_num 
            );
        }
    };
    
    
    /**
     * 获取birthDate关联的年龄.
     * @return {Number}
     * @method getAge
     * @static
     */
    Date.getAge = function(birthDate){
        if(Type.isString(birthDate)){
            birthDate = Date.from(birthDate);
        }    
        var today = new Date(), 
            age = today.getFullYear() - birthDate.getFullYear(),
            m = today.getMonth() - birthDate.getMonth();
        if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
            age--;
        }
        return age; 
    };
    
    
    if(!Date.tick){
        /**
         * 获取当前时间刻度.
         * @method tick
         * @static
         * @return {Number}
         */
        Date.tick = function(){
            return (new Date()).getTime();
        };
    }
    
    if(!Date.now){
        /**
         * 获取当前时间刻度.
         * Date.now() == (new Date()).getTime()
         * @method now
         * @static
         * @return {Number}
         */
        Date.now = function(){
            return Date.tick();
        };
    }
    
    /**
     * 获取当前时间对象.
     * @method nowDateObj
     * @static
     * @return {Date}
     */
    if(!Date.nowDateObj){
        Date.nowDateObj = function(){
            return new Date();
        };
    }
    
    /**
     * 获取两个时间差.
     * @method DateDiff
     * @static
     */
    if(!Date.DateDiff){
        Date.DateDiff = function( interval, date1, date2 ) { 
            if(!(date1 instanceof Date) ){
                date1 = Date.from(date1);
            }
            if(!(date2 instanceof Date) ){
                date2 = Date.from(date2);
            }
            var millisecondDiff = date2.getTime() - date1.getTime(); //相差毫秒数
            var monthDiff = ( date2.getFullYear() - date1.getFullYear() ) * 12 + ( date2.getMonth() - date1.getMonth() );
            switch( interval.toLowerCase() ) { 
                case "y" :
                case "year" :
                    return ( monthDiff / 12 ).toFixed(1);
                case "m" :
                case "month" :  
                    return monthDiff; 
                case "d" :
                case "day" : 
                    return parseInt( millisecondDiff/1000/60/60/24 ); 
                case "w" :
                case "week" : 
                    return parseInt( millisecondDiff/1000/60/60/24/7 ); 
                case "h" :
                case "hour" : 
                    return parseInt( millisecondDiff/1000/60/60 ); 
                case "n" :
                case "minute" : 
                    return parseInt( millisecondDiff/1000/60 );                 
                case "s" :
                case "second" : 
                    return parseInt( millisecondDiff/1000 );                 
                case "l" :
                case "millisecond" : 
                    return millisecondDiff; 
            } 
        };
    }
    
    /**
     * 解析字符串格式的时间，如：13-Nov-2013。 
     * @method parseStrDate
     * @static
     * @return {Date}
     */
    Date.parseStrDate = function(str){
        if(str){
            var parts = str.split('-');
            return new Date(
                parseInt(parts[2]),
                ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'].indexOf(parts[1]),
                parseInt(parts[0])
            );
        }
    };
    
    /**
     * 获取当前时间的时间段易读提示.
     * @param {Date|Number} time
     */
    Date.getTimeTooltip = function(time){
        if(time){
            var date = new Date(time);
            if(date instanceof Date){
                return date.getTimeTooltip(Date.timeTooltipFormat['en']);
            }
        }
        return '';
    };
    
    Date.formatDate = function(time){
        var date = time ? new Date(time) : new Date(), 
            format = window.ujsConfig ? ujsConfig.dateFormat : "dd-MN-yyyy";    
        return date.format(format);
    };
    
    Date.formatDateTime = function(time){
        var date = time ? new Date(time) : new Date(), 
            format = window.ujsConfig ? ujsConfig.dateTimeFormat : "dd-MN-yyyy hh:mm";    
        return date.format(format);
    };
    
    Date.nowStr = function(){
        return (new Date()).toLocaleString();
    };
    
    /**
     * 预定义的几种时间段格式.
     */
    Date.timeTooltipFormat = {
        'cn' : {
            'one' : '刚刚',
            'two' : 'MP分钟前',
            'three' : '今天 H:mm DP',
            'four' : 'M月dd日 H:mm DP',
            'five' : 'yy年MM月dd日 H:mm DP'    
        },
        'en' : {
            'one' : 'H:mm DP',
            'two' : 'H:mm DP',
            'three' : 'H:mm DP',
            'four' : 'H:mm DP M/dd',
            'five' : 'H:mm DP yy/MM/dd'    
        }
    }; 
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    /**
     * 是否包含某元素，或一系列元素。
     * @method contains
     * @return {Boolean} Copy of ...
     */
    Array.prototype.contains = function(obj/*, obj2, obj3, ...*/) {
        if(Type.isUndefined(obj)){
            return false;
        }
        if(arguments.length === 1){
            var i = this.length;
            while (i--) {
                if (this[i] === obj) {
                    return true;
                }
            }
            return false;   
        } else {
            for(var j=0, jLen=arguments.length; j<jLen; j++){
                if(!this.contains(arguments[j])){
                    return false;
                }
            }  
            return true;
        }
    };
    
    /**
     * 判断数组中是否至少包含一个指定的元素.
     * @method containsAtLeastOne
     * @return {Boolean}
     */
    Array.prototype.containsAtLeastOne = function(obj, obj2, obj3 /*, obj4, ...*/) {
        for(var i=0, iLen=arguments.length; i<iLen; i++){
            if(this.contains(arguments[i])){
                return true;
            }
        }  
        return false;
    };
    
    /**
     * 交换数组中两个元素的位置.
     * @method swapTwoItems
     * @return {Array}
     */
    Array.prototype.swapTwoItems = function(obj1, obj2){
        var pos1, pos2;
        for(var i=0, iLen=this.length; i<iLen; i++){
            if(this[i] === obj1) pos1 = i;
            if(this[i] === obj2) pos2 = i;
        }
        if(pos1 > -1 && pos2 > -1){
            this[pos1] = obj2;
            this[pos2] = obj1;
        }
        return this;
    };
    
    /**
    * 添加一个元素， 若已存在，则不添加。
    *
    * @method add
    * @param {Object|Array} obj
    * @return {Array} Array
    */
    Array.prototype.add = function(obj/*or arr*/){
        if( Type.isArray(obj) ){
            for(var i=0,len=obj.length; i<len; i++){
                this.add(obj[i]);
            }
        } else {
            if(!this.contains(obj)){
                this.push(obj);
            }
        }
        return this;
    };
    Array.prototype.append = Array.prototype.add;
    
    Array.prototype.sum = function(){
        var sum = 0;
        for(var i=0,len=this.length; i<len; i++){
            sum += (parseFloat(this[i]) || 0);
        }
        return sum;
    };
    
    /**
     * 在数组中的指定位置添加元素.
     * @method addAt
     * @param index {Number} 要插入的位置.
     * @param obj1, obj2, ..., objn {Mixed} 要添加的数据项.
     * @return {Array} 修改后的原数组.
     */
    Array.prototype.addAt = function(index, obj/*, obj2, obj3, ...*/){
        var args = Object.toArray(arguments);
        if(args.length < 3){
            this.splice(index, 0, obj);
        } else {
            args.splice(1, 0, 0);
            [].splice.apply(this, args);
        }
        return this;
    };
    
    /**
     * 移动数组中的元素到指定的位置.
     * @method move
     * @return {Array}
     */
    Array.prototype.move = function(obj, index){    
        //兼容function(obj, relObj, pos).
        if(arguments.length > 2){
            var relObj = index,
                pos = arguments[2],
                relObjIndex = this.indexOf(relObj);
            if(relObjIndex > -1){
                this.move(obj, pos < 1 ? relObjIndex : relObjIndex + 1);
            } else {
                this.move(obj, this.length);
            }
        } else {
            var originalIndex = this.indexOf(obj); 
            if(index !== originalIndex){
                //先添加元素到新位置，再删除掉老位置的元素。
                this.splice(index, 0, obj);
                if(originalIndex > -1){                
                    this.splice(index < originalIndex ? ++originalIndex : originalIndex, 1);
                }
            }
        }
        return this;
    };
    
    /**
     * 随机获取数组中的一个元素.
     * Math.random() -> 取[0,1)随机数.
     * @method getItemRandomly
     * @return {ArrayItem}
     */
    Array.prototype.getItemRandomly = function(){
        return this[Math.floor(Math.random() * this.length)];    
    };
    Array.prototype.random = Array.prototype.getItemRandomly;
    
    /**
     * 删除参数中指定的元素，参数格式如: ( obj1, obj2, obj3, ... )
     * @method remove
     * @return {Array}
     */
    Array.prototype.remove = function() {
        var items = arguments, 
            k = 0, 
            len = items.length, 
            item = null;
            
        for (; k < len; k++) {
            item = items[k];
            for (var i = 0, n = 0; i < this.length; i++) {
                if (this[i] != item) {
                    this[n++] = this[i];
                }
            }
            this.length = n;
        }
        return this;
    };
    /**
     * 删除指定位置的元素.
     * @method removeAt
     * @return {Array} : 被删除的元素.
     */
    Array.prototype.removeAt = function(index1, index2, index3 /*, index4, ...*/){    
        var indexArr = Object.toArray(arguments).unique().sortAsc().shrink(),
            indexItem;        
        while(!Type.isUndefined(indexItem = indexArr.pop())){
            this.splice(indexItem, 1);
        }   
        return this;
    };
    
    /**
     * 删除符合指定条件的数组项.
     * This function will remove any elements from the array which satisfy the conditions in the user provided function fn. 
     * If no function passed, undefined and null values are removed.
     * 本函数修改原数组.
     * @param fn {Function} : 条件函数
     */
    Array.prototype.removeIf = function(fn) {
        if (!fn){
            fn = function(i) {
                return i == undefined || i == null;
            };
        }    
        for(var n=0, i=0, l=this.length; i<l; i++){
            fn(this[i]) ? n++ : this[i-n] = this[i];
        }
        this.length = this.length - n;
        return this;
    };
    
    /**
     * 升序排列数组(适用Number与String类型的数组). 
     * @method sortAsc
     * @return {Array}
     */
    Array.prototype.sortAsc = function(){
        return this.sort(function(a,b){
            return a > b; 
        });
    };
    /**
     * 降序排列数组(适用Number与String类型的数组).
     * @method sortDesc
     * @return {Array}
     */
    Array.prototype.sortDesc = function(){
        return this.sort(function(a,b){
            return a < b;
        });
    };
    /**
     * 去除重复元素.
     * @method unique
     * @return {Array}
     */
    Array.prototype.unique = function() {    
        for (var i = 0, len = this.length; i < len; i++) {
            for (var j = i + 1; j < len; j++) {
                if (this[i] === this[j]) {
                    this.splice(j--, 1);
                    len--;
                }
            }
        }
        return this;
    };
    /**
     * 取两个Array的交集.
     * @method intersect
     * @return {Array}
     */
    Array.prototype.intersect = function(arr) {
        var twoArr = this.length > arr.length ? [this.unique(), arr] : [arr.unique(), this], 
            newArr = [], 
            longArr = twoArr[0], 
            shortArr = twoArr[1];
            
        for (var i=0,len=longArr.length; i<len; i++) {
            if (shortArr.contains(longArr[i])) {
                newArr.push(longArr[i]);
            }
        }
        return newArr;
    };
    
    /**
     * 获取数组第一个元素.
     * @method first
     * @return {ArrayItem}
     */
    Array.prototype.first = function(){
        return this[0];
    };
    
    /**
     * 获取数组的最后一个.
     * @method last
     * @return {ArrayItem}
     */
    Array.prototype.last = function() {
        return this.length ? this[this.length - 1] : null;
    };
    
    /**
     * 获取数组中间的一个.
     * @method middle
     * @return {ArrayItem}
     */
    Array.prototype.middle = function() {
        if(this.length > 0){
            return this[Math.floor((this.length-1)/2)];
        }
    };
    
    /**
     * 克隆数组.
     * @method clone
     * @return {Array}
     */
    Array.prototype.clone = function() {
        var arr = [];
        for (var i = 0; i < this.length; i++) {
            var item = this[i]; 
            arr[i] = item && item.clone ? item.clone() : item;
        }
        return arr;
    };
    /**
     * 去除数组中的冗余空元素.
     * @method shrink
     * @return {Array}
     */
    Array.prototype.shrink = function(){
        for(var i = 0, arrLen = this.length; i < arrLen; i++){
            var item = this[i];
            if(item === '' || item === null || item === undefined || (typeof item == 'number' && window.isNaN(item))){
                this.splice(i--,1);
                arrLen--;           
            }
        }
        return this;
    };
    /**
     * 将二维数组转化成一维数组.
     * 如: [[1,2,3],[4,5,6,[7,8]]].linear()   =>   [1,2,3,4,5,6,[7,8]]
     * @method linear
     * @return {Array}
     */
    Array.prototype.linear = function(){
        return [].concat.apply([], this);
    };    
    /**
     * 判断数组中是否至少有一个元素满足条件.
     * fn* ( Object, Number, Array ) : Boolean  --- callback, 
     * [ctx]  --- Object 
     * @method some
     * @return {Boolean}
     *
     * @example
     *      function isBigEnough(element, index, array) {
     *          return (element >= 10);
     *      }
     *      var passed = [2, 5, 8, 1, 4].some(isBigEnough);
     *      // passed is false
     *      var passed = [12, 5, 8, 1, 44].some(isBigEnough);
     *      // passed is true
     */
    Array.prototype.some || (Array.prototype.some = function(fn, ctx){
        for( var i = 0, len = this.length; i < len; i++){
            if( fn.call( ctx || window, this[i], i, this ) ){
                return true;
            }
        }
        return false;
    });
    
    /**
     * 判断数组中是否所有元素均满足条件.
     * fn* ( Object, Number, Array ) : Boolean  --- callback, 
     * [ctx]  --- Object
     * @method every
     * @return {Boolean} 
     */
    Array.prototype.every || (Array.prototype.every = function(fn, ctx){
        for( var i = 0, len = this.length; i < len; i++){
            if( !fn.call( ctx || window, this[i], i, this ) ){
                return false;
            }
        }
        return true;
    });
    
    /**
     * Creates a new array with all elements that meet the specified criteria.
     * fn* ( Object, Number, Array ) : Boolean  --- callback, 
     * [ctx]  --- Object
     * @method filter
     * @return {Array} 
     */
    Array.prototype.filter || (Array.prototype.filter = function(fn, ctx){
        var newArr = [];
        for( var i = 0, len = this.length; i < len; i++){
            if( fn.call( ctx || window, this[i], i, this ) ){
                newArr.push(this[i]);
            }
        }
        return newArr;
    });    
    Array.prototype.find = Array.prototype.filter;
    
    /**
     * 查找一个符合条件的元素.
     */
    Array.prototype.filterOne = function(fn, ctx){        
        for( var i = 0, len = this.length; i < len; i++){
            if( fn.call( ctx || window, this[i], i, this ) ){
                return this[i];
            }
        }
    };
    Array.prototype.findOne = Array.prototype.filterOne;
    
    /**
     * Executes the specified function once for each element in an array.
     * fn* ( Object, Number, Array ), 
     * [ctx]  --- Object
     * @method forEach
     * @return {Array} 
     */
    /**
     *  var writer = {
            sb:       [],
            write:    function (s) {
                this.sb.push(s);
            },
            writeln:  function (s) {
                this.write(s + "\n");
            },
            toString: function () {
                return this.sb.join(",");
            }
        };            
        
        [2, 5, 9].forEach( writer.write, writer );
        writer.toString();            
        // Prints:
        // 2
        // 5
        // 9        
     */
    Array.prototype.forEach || (Array.prototype.forEach = function(fn, ctx){
        for( var i = 0, len = this.length; i < len; i++){
            if(fn.call( ctx || this, this[i], i, this ) === false){
                break;
            }
        }
        return this;
    });
    Array.prototype.each = Array.prototype.forEach; 
    
    /**
     * Returns the first index number at which the specified element can be found in the array. 
     * Returns -1 if the element is not present. 
     * @method indexOf
     * @return {Number}
     */
    Array.prototype.indexOf || (Array.prototype.indexOf = function( searchElement, fromIndex ){
        var searchIndex = -1;
        for( var i = ( fromIndex || 0 ), len = this.length; i < len; i++){
            if( this[i] === searchElement ){
                searchIndex = i;
                break;
            }
        }
        return searchIndex;
    });    
    /**
     * Searches an array backwards starting from fromIndex and returns the last index number at which the specified element can be found in the array. 
     * Returns -1 if the element is not present. 
     * @method lastIndexOf
     * @return {Number}
     */
    Array.prototype.lastIndexOf || (Array.prototype.lastIndexOf = function( searchElement, fromIndex ){
        var searchIndex = -1;
        for( var i = ( fromIndex || this.length - 1 ); i > -1; i--){
            if( this[i] === searchElement ){
                searchIndex = i;
                break;
            }
        }
        return searchIndex;
    });    
    /**
     * Creates a new array with the results of calling a provided function on every element in this array.
     * fn* ( Object, Number, Array ), 
     * [ctx]  --- Object 
     * @method map
     * @return {Array}
     */
    Array.prototype.map || (Array.prototype.map = function(fn, ctx){
        var newArr = [];
        for( var i = 0, len = this.length; i < len; i++){
            newArr.push( fn.call( ctx || window, this[i], i, this ) );
        }
        return newArr;
    });    
    /**
     * 将数组内的元素次序打乱(洗牌).
     * @method shuffle
     * @return {Array}
     */
    Array.prototype.shuffle = function(){
        this.sort(function(){return Math.random() - 0.5;});
        return this;
    };        
    /**
     * 清空数组.
     * @method clear
     * @return {Array}
     */
    Array.prototype.clear = function(){
        this.length = 0;
        return this;
    };
    Array.prototype.empty = Array.prototype.clear;
       
    /**
     * 获取数组中最大的数组项.
     * @method getMaxItem
     * @return {ArrayItem}
     */
    Array.prototype.getMaxItem = function(){
        return Math.max.apply(Math, this);
    };       
    /**
     * 获取数组中最小的数组项.
     * @method getMinItem
     * @return {ArrayItem}
     */
    Array.prototype.getMinItem = function(){
        return Math.min.apply(Math, this);
    };    
    /**
     * Pad an array to a given size with a given value.<br/>
     * This function will return a copy of the array padded to length size with initial value.<br/>
     *  
     * If size is less than or equal to the length of the array a copy of the array is returned. <br/>
     * If size is negative the array is padded from the left. <br/>
     * If positive it is padded from the right.<br/>
     * 
     * @param size {Number} 数组容量.
     * @param val {Mixed} 数组填充值.
     * @method pad
     * @return {Array} 新数组实例.
     */
    Array.prototype.pad = function(size,val) {
        var l = Math.abs(size) - this.length;
        var a = [].concat(this);
        if (l <= 0){
            return a;
        }
        for(var i=0; i<l; i++){
            size < 0 ? a.unshift(val) : a.push(val);
        }
        return a;
    };    
    /**
     * 获取当前数组针对指定数组的差集.
     * @method subtract.
     * @return {Array}
     */
    Array.prototype.subtract = function(arr){
        var result = [];
        for(var i=0,len=this.length; i<len; i++){
            if(!arr.contains(this[i])){
                result.push(this[i]);
            }
        } 
        return result;
    };    
    /**
     * The difference is computed as the elements which are in the calling array but not in the arrays passed as arguments.<br/> 
     * Duplicates are removed from the result.
     * 
     * @method diff
     * @param arr0 - arrN {Array} ([ Array a0 ... an ]) 
     * @return {Array} A new array with those elements which are not in arrays a0 trough an.
     */
    Array.prototype.diff = function() {
        var a1 = this;
        var a = a2 = null;
        var n = 0;
        while(n < arguments.length) {
            a = []; //数组差集临时存储.
            a2 = arguments[n];
            var diff = true;
            for(var i=0,l = a1.length; i<l; i++) {
                for(var j=0,l2 = a2.length; j<l2; j++) {
                    if (a1[i] === a2[j]) {
                        diff = false;
                        break;
                    }
                }
                diff ? a.push(a1[i]) : diff = true;
            }
            a1 = a;
            n++;
        }
        return a.unique();
    };    
    /**
     * Check whether two arrays are disjoint<br/>
     * 
     * This functions checks whether two arrays are disjoint.<br/> 
     * If no element is in more than one array then the arrays are considered to be disjoint.<br/>
     * 
     * @method disjoint
     * @param {Array} 
     * @return {Boolean}
     */
    Array.prototype.disjoint = function() {
        var args = [];
        var l = arguments.length;
        if (!l)
            return true;
        for(var i=0; i<l; i++)
            args.push(arguments[i]);
        return Array.prototype.intersect.apply(this,args).length > 0 ? false : true;
    };    
    /**
     * 重写toString方法，去掉‘逗号’字符串连接符。
     * 默认toString方法将数组中的所有项转化成字符串，而后以‘逗号’分隔连接。
     * 大多数时，我们不需要这个‘逗号’连接符。
     * @method toString
     * @param sep {String} 字符串连接符，默认空字符串。
     * @return {String}
     */
    Array.prototype.toString = function(sep){
        return this.join(sep || '');
    };
    
    //------------------------------------------------------------------------------------------------------------
    
    /**
     * 合并多个数组为一个数组.
     * @static
     * @method merge
     * @return {Array}
     */
    Array.merge = function(){
        var arr = [],
            _pushLogic = Array.prototype.push,
            args = arguments;
        for(var i=0; i<args.length; i++){
            _pushLogic.apply(arr, args[i]);    
        }
        return arr; 
    };    
    /**
     * 判断指定的参数是否是一个数组.
     * @static
     * @method isArray
     * @return {Boolean}
     */
    Array.isArray || (Array.isArray = function(obj){
        return ( obj instanceof Array );
    });    
    /**
     * 将类数组对象转化为数组.
     * @param {Object} list
     * @static
     * @method toArray
     * @return {Array}
     */
    Array.toArray || (Array.toArray = function(list){
        return Object.toArray(list);
    });    
    /**
     * 取多个Array的交集.
     * @static
     * @method intersect
     * @return {Array}
     */
    Array.intersect = function(/* arr1, arr2, arr3, ... or [arr1, arr2, arr3, ...] */) {
        if (arguments[0]) {
            var args = Object.prototype.toString.call(arguments[0][0]) == '[object Array]' ? arguments[0] : arguments,
                newArr = args[0];        
            for (var i = 1, len = args.length; i < len; i++) {
                newArr = newArr.intersect(args[i]);
            }        
            return newArr;
        }
    };    
    /**
     * array-like enumeration
     * mozilla already supports this
     * @static
     * @method forEach
     * @return {Array}
     */
    Array.forEach || (Array.forEach = function(array, fn, context) {
        for(var i = 0, len = array.length; i < len; i++) {
            fn.call(context || array, array[i], i, array);
        }
        return array;
    });    
    /**
     * 将所有参数转化成一个一维数组。
     * @static
     * @method linear
     * @return {Array}
     */
    Array.linear = function(){    
        return [].slice.call(arguments, 0).linear();
    };    
    /**
     * 获取数组.
     * @static 
     * @param {Array|Mixed} arr
     * @param {Array}
     */
    Array.insure = function(arr){
        if(Type.isUndefined(arr)){
            return [];
        } else {
            if(!Type.isArray(arr)){
                arr = [arr];
            }
            return arr;
        }
    };    
    
    
    




























 /**
     * 继承.
     * @method extend
     * @param {Function} superFunction 父类.
     * @return {Function} 子类.
     */    
    Function.prototype.extend = function(superFunction, funName) {
        var _this = this;
        if (!superFunction) {
            return _this;
        }        
        var subFunction = eval([
            '(function {0}(){'.format( funName || _this.name || '' ),
                'superFunction.apply(this, arguments);',            
                '_this.apply(this, arguments);',  
            '})'
        ].join(''));
        //superFunction.prototype内可能有数据成员, 使用深拷贝。
        Object.extend(true, subFunction.prototype, superFunction.prototype);
        subFunction.prototype.constructor = subFunction;
        subFunction.superFunction = subFunction.prototype.superFunction = superFunction;
        return subFunction;        
    };
    
    /**
     * 检测指定的对象是否是当前构造函数的实例。
     * @method detectInstance
     * @param {Object} obj
     * @return {Boolean}
     */
    Function.prototype.detectInstance = function(obj){
        var func = obj.constructor;
        do{
            if(func === this){
                return true;
            } else {
                func = func.superFunction;
            }            
        } while(func);
        return false;
    };
    
    /**
     * Defer execution of a function.
     * 
     * This method will defer execution of the function ms milliseconds. 
     * If an object obj is provided then the function will be executed as a method on the object.
     * 
     * Any extra arguments a2...an will be passed along to the function.
     * 
     * @param ms {Number}
     * @param ctx {Object}
     */
    Function.prototype.defer = function(ms, ctx/*, [ Mixed a2...an ]*/) {
        // Get arguments as array
        var a = [];
        for(var i=2; i<arguments.length; i++){
            a.push(arguments[i]);
        }            
        var _this = this;
        return window.setTimeout(function(){
            return _this.apply(ctx, a);
        }, ms || 0);
    };
    
    // generic enumeration
    Function.prototype.forEach = function(object, fun, context) {
        for(var key in object) {
            if( typeof this.prototype[key] == "undefined") {
                fun.call(context, object[key], key, object);
            }
        }
    };
    
    /**
     * Return the body of a function
     */
    Function.prototype.getBody = function() {
        // Get content between first { and last }
        var m = this.toString().match(/\{([\s\S]*)\}/m)[1];
        // Strip comments
        return m.replace(/^\s*\/\/.*$/mg,'');
    };
    
    
    /**
     * Return the name of a function
     */
    Function.prototype.getName = function() {
        var m = this.toString().match(/^function\s(\w+)/);
        return m ? m[1] : "anonymous";
    };
    
    /**
     * Return the names of the arguments as strings in a an array
     */
    Function.prototype.getArguments = function() {
        // Get content between first ( and last )
        var m = this.toString().match(/\((.*)\)/)[1];
        // Strip spaces
        m = m.replace(/\s*/g,'');
        // String to array
        return m ? m.split(',') : [];
    };
    
    /**
     * Memoize (cache) results of a function call
     */
    Function.prototype.memoize = function(/*Mixed a0 ... an*/) {
        // If no calls yet
        if (!this.results)
            this.results = {};
        // Convert arguments object to array
        var args = Array.prototype.slice.call(arguments);
        // If results exist
        if (this.results[args])
            return this.results[args];
        // Else compute and store result
        this.results[args] = this.apply(null, args);
        // Return result of original function
        return this.results[args];
    };
    
    /**
     * 使函数在指定作用域下执行.
     */
    Function.prototype.bind = Function.prototype.bind || function(ctx){
        var _this = this,
            slice = Array.prototype.slice,
            args = slice.call(arguments, 1);        
        return function(){        
            return _this.apply(ctx, args);
        };
    };
    
    /**
     * 重写Setter, 使参数如setter(key, value)的方法支持setter({key1:value1, key2:value2})式的复数调用.
     * @param {Boolean} isUsePlural 是否仅支持复数调用.
     * @return {Function} 
     */
    Function.prototype.overloadSetter = function(isUsePlural){
        var _this = this;
        return function(key, value){
            if(key == null) return this;
            if(isUsePlural || typeof key != 'string'){
                var obj = key;
                for (var prop in obj){
                    _this.call(this, prop, obj[prop]);
                }     
            } else { 
                _this.call(this, key, value);
            }      
            return this;
        };
    };
    
    /**
     * 重写Getter, 使参数如getter(key)的方法支持getter(key1, key2, ...)|getter([key1, key2, ...])式的复数调用.
     * @param {Boolean} isUsePlural 是否仅支持复数调用.
     * @return {Function}
     */
    Function.prototype.overloadGetter = function(isUsePlural){
        var _this = this;
        return function(key){
            var args, result;       
            if (typeof key != 'string'){
                args = key;
            } else if (arguments.length > 1){
                args = arguments;
            } else if (isUsePlural){
                args = [ key ];
            }       
            if (args){
                result = {};
                for (var i = 0; i < args.length; i++){
                    result[args[i]] = _this.call(this, args[i]);
                }
            } else {
                result = _this.call(this, key);
            }
            return result;
        };
    };
    
    /**
     * 为构造函数添加原型成员.
     */
    Function.prototype.implement = function(key, value){
        this.prototype[key] = value;
    }.overloadSetter();
    
    //------------------------------------------------------------------------------------------------------------
    
    /**
     * 空函数
     */
    Function.empty = function(){};
    
    /**
     * 管子函数.
     */
    Function.pipe = function(){
        var args = arguments,
            argsLen = args.length;  
        if(argsLen){
            return argsLen === 1 ? args[0] : Array.toArray(args);
        }
    };
    
    /**
     * 将指定的参数转换为函数.
     */
    Function.from = function(obj){
      return typeof obj == 'function' ? obj : function(){
        return obj;
      };
    };
    
    // Returns a function, that, as long as it continues to be invoked, will not be triggered. 
    // The function will be called after it stops being called for N milliseconds. 
    // If `immediate` is passed, trigger the function on the leading edge, instead of the trailing.
    Function.debounce = function(func, wait, immediate) {
        var timeout;
        return function() {
            var context = this, 
                args = arguments;
        
            var later = function() {
                timeout = null;
                if (!immediate) func.apply(context, args);
            };
        
            var callNow = immediate && !timeout;
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
            if (callNow) func.apply(context, args);
        };
    };